#include "camera.h"

camera::camera()
{
	camPosition		= point(0.0);
	gridPosition	= point(0.0,0.0,1.0);
	Up = vector3(0.0,1.0,0.0);
	standardSet();
}
camera::camera(point pos, point grid)
{
	camPosition		= pos;
	gridPosition	= grid;
	Up = vector3(0.0,1.0,0.0);
	standardSet();

}
camera::camera(std::string fileName)
{
	std::ifstream plik;

	plik.open(fileName.c_str());

	if (!plik)
	{
		std::cout<<"XMLFAIL";
		standardSet();
		return;
	}
	standardSet();
	char line[MAX_LENGTH];
	plik.getline(line, MAX_LENGTH);

	while(plik!=NULL)
	{
		plik.getline(line, MAX_LENGTH);
		std::string temp(line);
		unsigned int i(0);

		while(temp[i] == 9 || temp[i] == 32)
			++i;

		if(temp[i] == '<')
		{
			++i;
			std::string nazwaWezla;
			std::string nazwaWezlaKoniec;
			std::string zawartoscWezla;

			while(temp[i] != '>')
			{
				nazwaWezla.push_back(temp[i]);
				++i;
			}
			if(nazwaWezla == "camera")
			{
				//yay idziemy dalej
				continue;
			}
			else if(nazwaWezla == "camPosition")
			{
				camPosition.getPointFromXML(plik);
				continue;
			}
			else if(nazwaWezla == "gridPosition")
			{
				gridPosition.getPointFromXML(plik);
				continue;
			}
			else if(nazwaWezla == "Up")
			{
				Up.getVector3FromXML(plik);
				continue;
			}
		}
	}
}
camera::~camera()
{

}
void camera::standardSet()
{
	if(camPosition != gridPosition)
		At = vector3(camPosition, gridPosition);
	else
		At = vector3(0.0,0.0,1.0);

	rotationMatrix.identity();
	long double angleX(0.0);
	long double angleY(0.0);
	long double angleZ(0.0);
	
	if(At.z != 0.0)
		angleX = -atan2((long double)At.y , (long double)At.z );
	if(At.z != 0.0)
		angleY = atan2((long double)At.x , (long double)At.z );
	if(Up.y != 0.0)
		angleZ = atan2((long double)Up.x , (long double)Up.y );

	rotationMatrix.rotateByAxisX(angleX);
	rotationMatrix.rotateByAxisY(angleY);
	rotationMatrix.rotateByAxisZ(angleZ);
}
void camera::setRenderoptions(renderoptions* opt)
{
	proprties = opt;
}
bool camera::render(scene* Scena, picture* Pic, std::string picName)
{
	switch(proprties->getCameraType())
	{
		case 0: perspectiveRender(Scena, Pic, picName);break;
		case 1: ortographicRender(Scena, Pic, picName);break;
		//case 2: pinholeRender(Scena, Pic, picName);break;
		//case 3: fisheyeRender(Scena, Pic, picName);break;
		default: perspectiveRender(Scena, Pic, picName);break;
	}
	return true;
}
void camera::perspectiveRender(scene* Scena, picture* Pic, std::string picName)
{
	double szerokoscPixela = 2.0 / proprties->getWidth();
	double wysokoscPixela = 2.0 / proprties->getHeight();

	if ( proprties->getWidth() >  proprties->getHeight())
	{
		szerokoscPixela = 2.0 / proprties->getWidth();
		wysokoscPixela = szerokoscPixela*proprties->getpixelAspectRatio();
	}
	else
	{
		szerokoscPixela = 2.0 / proprties->getHeight();
		wysokoscPixela = szerokoscPixela*proprties->getpixelAspectRatio();
	}

	double srodekX(0);
	double srodekY(0);
	bool trafil(false);
	double pixelCount(proprties->getWidth()*proprties->getHeight());
	double pixelsDone(0);
	
	double H( 1.0/ tan(proprties->getFOV()));
	point estimatedGridPosition = gridPosition;//*H;

	for (unsigned int i(0); i <proprties->getWidth(); i++)
	{
		
		for (unsigned int  j(0); j < proprties->getHeight(); j++)
		{
			std::cout<<"\r"<<(pixelsDone/pixelCount)*100;
			pixelsDone++;	
			srodekX = -1.0f + (i + 0.5f) * szerokoscPixela;
			srodekY = 1.0f - (j + 0.5f) * wysokoscPixela;

			point point1(srodekX, srodekY, 0);
			point1+=estimatedGridPosition;

			vector3 crossAtUp = At;
			crossAtUp.cross(Up);
			crossAtUp.normalize();
			
			switch (proprties->getAntialasingAlgorithm())
			{
				case 0: Pic->setPixel(i, j, noneAntialiasing(Scena, Pic, camPosition, point1)); break;
				case 1: Pic->setPixel(i, j, linearAntialiasing(Scena, Pic, camPosition, point1, szerokoscPixela, wysokoscPixela)); break;
				case 2: Pic->setPixel(i, j, randomAntialiasing(Scena, Pic, camPosition, point1, szerokoscPixela, wysokoscPixela)); break;
				case 3: Pic->setPixel(i, j, adaptiveAntialiasing(Scena, Pic, camPosition, point1, szerokoscPixela, wysokoscPixela)); break;
				default: Pic->setPixel(i, j, noneAntialiasing(Scena, Pic, camPosition, point1)); break;
			}
		}
	}
	Pic->saveBMP(picName.c_str());	
}
void camera::ortographicRender(scene* Scena, picture* Pic, std::string picName)
{
	double szerokoscPixela = 2.0 / proprties->getWidth();
	double wysokoscPixela = 2.0 / proprties->getHeight();

	if ( proprties->getWidth() >  proprties->getHeight())
	{
		szerokoscPixela = 2.0 / proprties->getWidth();
		wysokoscPixela = szerokoscPixela*proprties->getpixelAspectRatio();
	}
	else
	{
		szerokoscPixela = 2.0 / proprties->getHeight();
		wysokoscPixela = szerokoscPixela*proprties->getpixelAspectRatio();
	}

	double srodekX(0);
	double srodekY(0);
	bool trafil(false);

	At = At * rotationMatrix;

	for (unsigned int i(0); i <proprties->getWidth(); i++)
	{
		for (unsigned int  j(0); j < proprties->getHeight(); j++)
		{
			srodekX = -1.0f + (i + 0.5f) * szerokoscPixela;
			srodekY = 1.0f - (j + 0.5f) * wysokoscPixela;

			point point1(srodekX, srodekY, 0);
			point point2(srodekX, srodekY, 1);

			point1+=camPosition;
			point2+=gridPosition;

			switch (proprties->getAntialasingAlgorithm())
			{
				case 0: Pic->setPixel(i, j, noneAntialiasing(Scena, Pic, point1, point2)); break;
				case 1: Pic->setPixel(i, j, linearAntialiasing(Scena, Pic, point1, point2, szerokoscPixela, wysokoscPixela)); break;
				case 2: Pic->setPixel(i, j, randomAntialiasing(Scena, Pic, point1, point2, szerokoscPixela, wysokoscPixela)); break;
				case 3: Pic->setPixel(i, j, adaptiveAntialiasing(Scena, Pic, point1, point2, szerokoscPixela, wysokoscPixela)); break;
				default: Pic->setPixel(i, j, noneAntialiasing(Scena, Pic, point1, point2)); break;
			}
		}
	}
	Pic->saveBMP(picName.c_str());
}

color camera::noneAntialiasing(scene* Scena, picture* Pic, point point1, point point2)
{
	intersection blabla;
	if (Scena->detectColison(ray(point1,point2),&blabla))
		return blabla.objectColor;
	else
		return color(0.0f);
}
color camera::linearAntialiasing(scene* Scena, picture* Pic, point point1, point point2, double pixWid, double pixHei)
{
	std::vector<color*> colorSamples;
	std::vector<point*> antialsiaingPoints;
	double pixWid4 = pixWid/4;
	double pixHei4 = pixHei/4;
	double pixWid8 = pixWid/8;
	double pixHei8 = pixHei/8;
	double pixWid12 = pixWid/12;
	double pixHei12 = pixHei/12;

	switch( proprties->getAntialasingMultiplyier() )
	{
	case 0: // actualy none;
		antialsiaingPoints.push_back( new point(point2));
		break;
	case 1: // 1x meaning more than none antialaising -> 2 samples
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4, 0.0	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4	, 0.0	,0.0 )));
		break;
	case 2: // 2x meaning more than 2 samples -> 4 samples
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4, pixHei4	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4, -pixHei4	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4	, pixHei4	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4	, -pixHei4	,0.0 )));
		break;
	case 3: // 3x meaning more than 4 samples -> 8 samples will be great ... or 9 perheps ?  :P Legend will tell
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4, pixHei4	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4, 0.0		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4, -pixHei4	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(0.0		, pixHei4	,0.0 )));
		antialsiaingPoints.push_back( new point(point2));
		antialsiaingPoints.push_back( new point(point2.move(0.0		, -pixHei4	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4	, pixHei4	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4	, 0.0		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4	, -pixHei4	,0.0 )));
		break;
	case 4: // 4x meaning more than  8 samples -> 16 samples
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8*3	, pixHei8*3		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8*3	, pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8*3	, -pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8*3	, -pixHei8*3	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(-pixWid8	, pixHei8*3		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8	, pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8	, -pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8	, -pixHei8*3	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(pixWid8		, pixHei8*3		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8		, pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8		, -pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8		, -pixHei8*3	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(pixWid8*3	, pixHei8*3		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8*3	, pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8*3	, -pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8*3	, -pixHei8*3	,0.0 )));
		break;
	case 5: // 5x meaning more than 16 samples -> 25 samples
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8*3	, pixHei8*3		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8*3	, pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8*3	, -pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8*3	, -pixHei8*3	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(-pixWid4	, pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4	, 0.0			,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4	, -pixHei4		,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(-pixWid8	, pixHei8*3		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8	, pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8	, -pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid8	, -pixHei8*3	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(0.0			, pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2));
		antialsiaingPoints.push_back( new point(point2.move(0.0			, -pixHei4		,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(pixWid8		, pixHei8*3		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8		, pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8		, -pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8		, -pixHei8*3	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(pixWid4		, pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4		, 0.0			,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4		, -pixHei4		,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(pixWid8*3	, pixHei8*3		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8*3	, pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8*3	, -pixHei8		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid8*3	, -pixHei8*3	,0.0 )));
		break;
	case 6: // 6x meaning more than 25 samples -> 36 samples
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12*5	, pixHei12*5	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12*5	, pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12*5	, pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12*5	, -pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12*5	, -pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12*5	, -pixHei12*5	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(-pixWid4	, pixHei12*5	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4	, pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4	, pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4	, -pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4	, -pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid4	, -pixHei12*5	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(-pixWid12	, pixHei12*5	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12	, pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12	, pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12	, -pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12	, -pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(-pixWid12	, -pixHei12*5	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(pixWid12	, pixHei12*5	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid12	, pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid12	, pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid12	, -pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid12	, -pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid12	, -pixHei12*5	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(pixWid4		, pixHei12*5	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4		, pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4		, pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4		, -pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4		, -pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid4		, -pixHei12*5	,0.0 )));

		antialsiaingPoints.push_back( new point(point2.move(pixWid12*5	, pixHei12*5	,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid12*5	, pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid12*5	, pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid12*5	, -pixHei12		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid12*5	, -pixHei4		,0.0 )));
		antialsiaingPoints.push_back( new point(point2.move(pixWid12*5	, -pixHei12*5	,0.0 )));

		break;
	default: // ... imposible, but still, I'm not as smart as it seams :P  if something went wrong -> 1 sample
		antialsiaingPoints.push_back( new point(point2));
		break;
	}
	for (unsigned int k(0); k < antialsiaingPoints.size(); k++)
	{
		intersection blabla;
		ray sample(point1,*antialsiaingPoints.at(k));
		sample.setDirection(sample.getDirection()*rotationMatrix);
		if (Scena->detectColison(sample,&blabla))
			colorSamples.push_back( new color(blabla.objectColor));
		delete antialsiaingPoints.at(k);
	}
	antialsiaingPoints.~vector();

	color tempColor(0.0f);
	for (unsigned int c(0); c < colorSamples.size(); c++)
	{
		tempColor.add(*colorSamples.at(c));	//Using add because + operator lunches the Validate function which is undesired here.
		delete colorSamples.at(c);
	}
	int ghjk(colorSamples.size());
	colorSamples.~vector();
	tempColor/=ghjk;
	tempColor.validate();

	return tempColor;
}
color camera::randomAntialiasing(scene* Scena, picture* Pic, point point1, point point2, double pixWid, double pixHei)
{
	return color();
}
color camera::adaptiveAntialiasing(scene* Scena, picture* Pic, point point1, point point2, double pixWid, double pixHei)
{
	return color();
}